package com.zhny.aiot.zx.fertilizer.ic.plugin.device;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;
import com.zhny.aiot.fertilizer.ic.plugin.FertilizerICEngine;
import com.zhny.aiot.fertilizer.ic.plugin.device.FertilizerICConfig;
import com.zhny.aiot.fertilizer.ic.plugin.device.FertilizerICDevice;
import com.zhny.aiot.fertilizer.ic.plugin.model.FertilizerPacket;
import com.zhny.aiot.fertilizer.ic.plugin.model.FertilizerPacketRequest;
import com.zhny.aiot.fertilizer.ic.plugin.model.FertilizerPacketResponse;
import com.zhny.aiot.plugin.ICenterFacilities;
import com.zhny.aiot.plugin.IDeviceFactory;
import com.zhny.aiot.plugin.core.model.IRequestMessage;
import com.zhny.aiot.plugin.core.utils.MessageUtils;
import com.zhny.aiot.plugin.model.DeviceEntity;
import com.zhny.aiot.plugin.model.MessageEntry;
import com.zhny.aiot.plugin.model.MessageType;
import com.zhny.aiot.plugin.model.TaskEntity;
import com.zhny.aiot.zx.fertilizer.ic.plugin.model.FertilizerIcStoreEntry;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.util.internal.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

public class FertilizerIcDevice extends FertilizerICDevice<FertilizerIcStoreEntry> {
    private static final Logger logger=LoggerFactory.getLogger (FertilizerIcDevice.class);
    private final Map<String, Integer> CommandOperations;

    public FertilizerIcDevice(FertilizerICEngine engine, IDeviceFactory<FertilizerICConfig> factory, ICenterFacilities centerFacilities, DeviceEntity entity) {
        super (engine, factory, centerFacilities, entity);
        CommandOperations=new ImmutableMap.Builder<String, Integer> ()
                .put ("return", 0x31)
                .build ();
    }

    @Override
    public void onDecoder(FertilizerPacketResponse fertilizerPacket) {
        logger.info ("---start decoder fertilizer ic message---");
        MessageEntry resultMsg=MessageUtils.createMessageEntry (this.getId ());
        resultMsg.setType (MessageType.RESULT);
        int len=fertilizerPacket.getLength ();
        FertilizerIcStoreEntry storeEntry=null;
        try {
            if(len != fertilizerPacket.getData ().readableBytes ()) {
                logger.error ("fertilizer ic canRead length miss");
                resultMsg.setType (MessageType.ERROR);
                nofity (resultMsg);
            }
            if(fertilizerPacket.getCommand () == 0x30) {
                storeEntry=getStoreEntry (fertilizerPacket.getData ());
                resultMsg.setData (storeEntry);
            }else if(fertilizerPacket.getCommand () == 0x31) {
                storeEntry=getStoreEntry (fertilizerPacket.getData ());
                resultMsg.setData (storeEntry);
            }
            resultMsg.setProperties (new ImmutableMap.Builder<String, Object> ()
                    .put ("command", fertilizerPacket.getCommand ())
                    .put ("sequence", fertilizerPacket.getSequence ())
                    .put ("cardNumber", fertilizerPacket.getCardNumber ())
                    .put ("id", fertilizerPacket.getId ())
                    .put ("pressure", storeEntry.getPressure ())
                    .put ("flow", storeEntry.getFlow ())
                    .put ("liquidLevel", storeEntry.getLiquidLevel ())
                    .put ("pressureStatus", storeEntry.getPressureStatus ())
                    .put ("flowStatus", storeEntry.getFlowStatus ())
                    .put ("liquidLevelStatus", storeEntry.getLiquidLevelStatus ())
                    .put ("workStatus", storeEntry.getWorkStatus ())
                    .put ("powerStatus", storeEntry.getPowerStatus ())
                    .build ()
            );
            logger.info ("store:{}", storeEntry.toString ());
            save (storeEntry);
            saveReal (storeEntry);
            nofity (resultMsg);
            logger.info ("---stop decoder fertilizer ic message---");
        }finally {
            ByteBuf data = fertilizerPacket.getData();
            if (data != null && data.refCnt() > 0) {
                data.release();
            }
        }

    }

    private FertilizerIcStoreEntry getStoreEntry(ByteBuf data) {
//        byte[] bytes = new byte[6];
        try {
        data.readBytes (6);
        data.readBytes (6);
        Float pressure=data.readFloatLE ();
        Float flow=data.readFloatLE ();
        Float liquidLevel=data.readFloatLE ();
        int pressureStatus=data.readByte () & 0xff;
        int flowStatus=data.readByte () & 0xff;
        int liquidLevelStatus=data.readByte () & 0xff;
        int workStatus=data.readByte () & 0xff;
        int powerStatus=data.readByte () & 0xff;
        FertilizerIcStoreEntry storeEntry=new FertilizerIcStoreEntry ();
        storeEntry.setPressure (String.valueOf (pressure));
        storeEntry.setFlow (String.valueOf (flow));
        storeEntry.setLiquidLevel (String.valueOf (liquidLevel));
        storeEntry.setPressureStatus (pressureStatus == 0 ? "正常" : "异常");
        storeEntry.setFlowStatus (flowStatus == 0 ? "正常" : "异常");
        storeEntry.setLiquidLevelStatus (liquidLevelStatus == 0 ? "正常" : "异常");
        storeEntry.setWorkStatus (workStatus == 0 ? "空闲" :
                workStatus == 1 ? "工作正常" :
                        workStatus == 2 ? "手动正常结束" : "异常结束");
        storeEntry.setPowerStatus (powerStatus == 0 ? "市电正常" : "市电断电");

        return storeEntry;
        } finally {
            // 确保 ByteBuf 被释放
            if (data != null && data.refCnt() > 0) {
                data.release();
            }
        }
    }

    @Override
    protected IRequestMessage getCommand(TaskEntity taskEntity) {
        if(StringUtil.isNullOrEmpty (taskEntity.getMethodName ())) {
            logger.error ("执行命令名称为空");
            return null;
        }
        if(StringUtil.isNullOrEmpty (taskEntity.getParams ())) {
            logger.error ("执行命令参数为空");
            return null;
        }
        MessageEntry entry=MessageUtils.createMessageEntry (this.getId ());
        entry.getProperties ().put ("task_name", taskEntity.getMethodName ());
        entry.setType (MessageType.ERROR);
        try {
            if(!CommandOperations.containsKey (taskEntity.getMethodName ())) {
                entry.setData ("请传入正确设备的命令");
                return null;
            }
            return getPacket (taskEntity.getParams ());
        } catch (Exception e) {
            entry.setData (e.getMessage ());
            return null;
        } finally {
            this.nofity (entry);
        }
    }

    private FertilizerPacketRequest getPacket(String taskJson) {
//        String json ="{\"sequence\":\"250\",\"password\":\"123456\",\"balance\":500.0,\"price\":12.0,\"fertilizerAmount\":13.75,\"totalFertilizerAmount\":\"50\",\"cardNumber\":\"0\"}";
        Map<String, Object> map=this.serialize (taskJson, new TypeReference<HashMap<String, Object>> () {
        });
        check(map);
        long cardNumber=Long.parseLong (map.get ("cardNumber").toString ());
        int command= 0x31;
        int id=Integer.parseInt (this.getConfig ().getAddress ());
        int sequence=Integer.parseInt (map.get ("sequence").toString ());
        ByteBuf data=Unpooled.buffer ();
        LocalDateTime now = LocalDateTime.now();
        byte year = (byte) (now.getYear()%100); // 年份偏移量
        byte month = (byte) now.getMonthValue();     // 月份
        byte day = (byte) now.getDayOfMonth();       // 日
        byte hour = (byte) now.getHour();            // 小时
        byte minute = (byte) now.getMinute();        // 分钟
        byte second = (byte) now.getSecond();        // 秒

        // 写入时间数据到 ByteBuf
        data.writeByte (year);
        data.writeByte(month);
        data.writeByte(day);
        data.writeByte(hour);
        data.writeByte(minute);
        data.writeByte(second);
        //写入用户密码
        int pwd = Integer.parseInt (map.get ("password").toString ());
        data.writeInt (pwd);
        //写入单价
        Float price = Float.parseFloat (map.get ("price").toString ());
        data.writeFloatLE (price);
        //写入余额
        Float balance = Float.parseFloat (map.get ("balance").toString ());
        data.writeFloatLE (balance);
        //写入施肥量
        Float fertilizerAmount = Float.parseFloat (map.get ("fertilizerAmount").toString ());
        data.writeFloatLE (fertilizerAmount);
        //写入总肥量
        Float totalFertilizerAmount = Float.parseFloat (map.get ("totalFertilizerAmount").toString ());
        data.writeFloatLE (totalFertilizerAmount);
        int length = data.readableBytes ();
        byte[] bytes = new byte[data.readableBytes ()];
        data.readBytes (bytes);



        logger.info ("---packet  pwd:{},price:{},balance:{},fertilizerAmount:{},totalFertilizerAmount:{}---",
                pwd,price,balance,fertilizerAmount,totalFertilizerAmount);
        return new FertilizerPacketRequest (cardNumber, command, id, sequence, length, bytes);
    }

    private void check(Map<String,Object> map){
        if(map == null)
            throw new RuntimeException ("json serialize error");
        if(!map.containsKey ("cardNumber"))
            throw new RuntimeException ("IC卡号不存在！");
        if(!map.containsKey ("password"))
            throw new RuntimeException ("用户密码不存在！");
        if(!map.containsKey ("price"))
            throw new RuntimeException ("单价信息不存在！");
        if(!map.containsKey ("balance"))
            throw new RuntimeException ("余额信息不存在！");
        if(!map.containsKey ("fertilizerAmount"))
            throw new RuntimeException ("施肥量信息不存在！");
        if(!map.containsKey ("totalFertilizerAmount"))
            throw new RuntimeException ("罐内总肥量信息不存在！");
    }

    public static <T> T serialize(String json, TypeReference<T> typeReference) {
        if(StringUtil.isNullOrEmpty (json))
            return null;
        ObjectMapper mapper=new ObjectMapper ();
        try {
            mapper.configure (DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            T obj=mapper.readValue (json, typeReference);
            return obj;
//            return mapper.writeValueAsString(this);
        } catch (IOException e) {
            return null;
        }
    }

    @Override
    protected void disposable() {

    }
}
